using System;
using System.Text;
using System.Reflection;
using System.Collections.Generic;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

using RealDolmen.SharePoint.Helpers;
using System.Xml;

namespace RealDolmen.SharePoint.ListFeatures.Elements
{
    public enum ActivationMode
    {
        Automatic,
        Manual
    }
    public class ListFeatureTimerJobElement : ListFeatureElement, IListFeatureTrackerReferencee
    {
        public enum Keys
        {
            ScheduleString,
            ServerString
        }

        public static string RootTag
        {
            get { return "TimerJob"; }
        }

        #region Overrides
        public override string FullKey
        {
            get { return ListFeatureKeyManager.GenerateDottedKey("TimerJobs", Key); }
        }
        public override string Key
        {
            get { return Name; }
        }

        public override bool Customizable
        {
            get { return true; }
        }
        #endregion // Overrides

        #region Activate / Deactivate
        public override void Activate(ListFeature listFeature)
        {
            if (ActivationMode == ActivationMode.Automatic)
            {
                // SPList.ParentWeb should not be disposed
                // SPWeb.Site should not be disposed
                SPWebApplication webApplication = listFeature.ParentList.ParentWeb.Site.WebApplication;
                SPJobDefinition timerJob =
                    ListFeatureTimerJobElement.CreateJob(this, webApplication, listFeature);

                UpdateTrackerReference(listFeature, timerJob);
            }
        }

        private static void UpdateTrackerReference(ListFeature listFeature, SPJobDefinition timerJob)
        {
            if (timerJob is ListFeatureJobDefinition)
            {
                ListFeatureJobDefinition lfTimerJob =
                    timerJob as ListFeatureJobDefinition;
                
                // 08/07/2009 - ToNys: Added RunWithElevatedPrivileges
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    lfTimerJob.ListFeatureTrackerData = listFeature.TrackerData;
                    lfTimerJob.Update();
                });
            }
        }

        public override void Deactivate(ListFeature listFeature)
        {
            if (ActivationMode == ActivationMode.Automatic)
            {
                // SPList.ParentWeb should not be disposed
                // SPWeb.Site should not be disposed
                SPWebApplication webApplication = listFeature.ParentList.ParentWeb.Site.WebApplication;
                ListFeatureTimerJobElement.DeleteJob(this, webApplication, listFeature);
            }
        }
        #endregion // Activate / Deactivate

        #region Helpers
        protected SPJobDefinition GetJobDefinition(ListFeature listFeature)
        {
            SPJobDefinition jobDefinition = null;
            try
            {
                SPWebApplication webApplication = listFeature.ParentList.ParentWeb.Site.WebApplication;
                jobDefinition = GetJob(this, webApplication, listFeature);
            }
            catch { }
            return jobDefinition;
        }

        protected string GenerateJobName(ListFeature listFeature)
        {
            if (listFeature == null)
                return Name;
            else
                return string.Format("{0}.{1}", Name, listFeature.Id);
        }
        protected string GenerateJobTitle(ListFeature listFeature)
        {
            if (listFeature == null)
                return Title;
            else
                return string.Format("List Feature - {0}: {1}", listFeature.GetTitle(SPHelper.CurrentCultureInfo), Title);
        }
        #endregion // Helpers

        #region Public Properties
        public string Assembly
        {
            get { return XmlDefinition["Assembly"].InnerText; }
        }

        public string Class
        {
            get { return XmlDefinition["Class"].InnerText; }
        }

        public SPJobLockType JobLockType
        {
            get
            {
                try
                {
                    return (SPJobLockType)Enum.Parse(typeof(SPJobLockType), XmlDefinition["JobLockType"].InnerText);
                }
                catch
                {
                    return SPJobLockType.None;
                }
            }
        }

        public string Name
        {
            get { return XmlDefinition["Name"].InnerText; }
        }

        public string Title
        {
            get { return XmlDefinition["Title"].InnerText; }
        }

        public SPServer Server
        {
            get { return SPFarm.Local.Servers[ServerString]; }
        }

        public ActivationMode ActivationMode
        {
            get
            {
                string activationMode = GetDefaultAttribute("ActivationMode");
                if (string.IsNullOrEmpty(activationMode))
                    return ActivationMode.Automatic;
                else
                    return (ActivationMode)Enum.Parse(typeof(ActivationMode), activationMode);
            }
        }

        public string ServerString
        {
            set
            {
                if (value != null)
                    SetElement("Server", value);
            }
            get
            {
                string serverString = GetElement("Server");

                if (string.IsNullOrEmpty(serverString))
                {
                    SPServerCollection servers = SPFarm.Local.Servers;
                    foreach (SPServer server in servers)
                    {
                        if (server.Role == SPServerRole.Application && string.IsNullOrEmpty(serverString))
                        {
                            serverString = server.Name;
                            SetElement("Server", serverString);
                        }
                    }
                }

                return serverString;
            }
        }

        public SPSchedule Schedule
        {
            // 23/06/2008: try to parse onetimeschedules too
            get { return SPHelper.ScheduleFromString(ScheduleString); }
        }

        public string ScheduleString
        {
            set
            {
                if (value != null)
                {
                    // 23/06/2008: try to parse onetimeschedules too
                    SPSchedule schedule = SPHelper.ScheduleFromString(value);

                    if (schedule != null)
                    {
                        SetElement("Schedule", value);

                        if (Feature != null)
                        {
                            SPJobDefinition job = GetJobDefinition(Feature);

                            if (job != null)
                            {
                                // 08/07/2009 - ToNys: Added RunWithElevatedPrivileges
                                SPSecurity.RunWithElevatedPrivileges(delegate()
                                {
                                    job.Schedule = schedule;
                                    job.Update();
                                });
                            }
                        }
                    }
                }


            }
            get { return GetElement("Schedule"); }
        }

        #endregion // Public Properties


        #region JobDefinition Statics
        /*
        public static SPJobDefinition EnsureJobDefinition(ListFeature listFeature, string name, SPWebApplication webApplication)
        {
            List<ListFeatureTimerJobElement> list =
                listFeature.Elements.Get<ListFeatureTimerJobElement>();

            foreach (ListFeatureTimerJobElement timerJobElement in list)
            {
                if (timerJobElement.Name == name)
                {
                    return CreateJob(timerJobElement, webApplication, listFeature);
                    /*
                    timerJobElement.Activate(listFeature, true);
                    return timerJobElement.GetJobDefinition(listFeature);
                     //*
                }
            }
            return null;
        }
        */
        public static void DeleteJob(ListFeatureTimerJobElement element, SPWebApplication webApplication, ListFeature listFeature)
        {
            SPJobDefinition job = GetJob(element, webApplication, listFeature);

            if (job != null)
                job.Delete();
        }

        public static SPJobDefinition CreateJob(ListFeatureTimerJobElement element, SPWebApplication webApplication, ListFeature listFeature)
        {
            // Assembly information
            Assembly assembly = System.Reflection.Assembly.Load(element.Assembly);
            Type type = assembly.GetType(element.Class);

            if (GetJob(element, webApplication, listFeature) != null)
                return null;

            if (type != null)
            {
                if (!string.IsNullOrEmpty(element.ServerString))
                {
                    // Create the job
                    SPJobDefinition timerJob = null;

                    // 08/07/2009 - ToNys: Added RunWithElevatedPrivileges
                    SPSecurity.RunWithElevatedPrivileges(delegate()
                    {
                        timerJob = (SPJobDefinition)Activator.CreateInstance(type,
                                 element.GenerateJobName(listFeature),
                                 webApplication,
                                 element.Server,
                                 element.JobLockType);


                        timerJob.Title = element.GenerateJobTitle(listFeature);
                        timerJob.Schedule = element.Schedule;
                        timerJob.Update();
                    });
                    return timerJob;
                }
                else
                    throw new Exception("Please give an application server to run the timerjob on.");
            }
            else throw new DllNotFoundException(string.Format("Could not find class: {0}", element.Class));
        }

        public static SPJobDefinition GetJob(ListFeatureTimerJobElement element, SPWebApplication webApplication, ListFeature listFeature)
        {
            string jobName = element.GenerateJobName(listFeature);
            foreach (SPJobDefinition job in webApplication.JobDefinitions)
            {
                if (job.Name == jobName)
                {
                    return job;
                }
            }
            return null;
        } 
        #endregion // JobDefinition Statics


        #region Other Statics
        public static ListFeatureTimerJobElement GetTimerJobElement(ListFeatureDefinition definition, string name)
        {
            List<ListFeatureTimerJobElement> jobs = definition.Elements.Get<ListFeatureTimerJobElement>();

            foreach (ListFeatureTimerJobElement job in jobs)
            {
                if (job.Name == name)
                    return job;
            }
            return null;
        } 
        #endregion // Other Statics

        #region IListFeatureTrackerReference Members

        public void UpdateTrackerReference(ListFeature listFeature, string oldTrackerData)
        {
            SPWebApplication webApplication = listFeature.ParentList.ParentWeb.Site.WebApplication;
            SPJobDefinition job = GetJob(this, webApplication, listFeature);
            UpdateTrackerReference(listFeature, job);
        }

        #endregion
    }
}
